package com.javasea.log4j2.aop;

import com.javasea.log4j2.entity.ProviderQuery;
import com.javasea.log4j2.util.JacksonUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * xxl-job任务统一AOP处理
 *
 * @version 1.0 2023/3/6 19:05  伍波  创建类
 * @author: 伍波
 * @Date: 2023/3/6 19:05
 * @Version 1.0
 */
@Component
@Aspect
public class JFLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(JFLogAspect.class);

    @Pointcut("@annotation(jfLog)")
    public void pointCut(JFLog jfLog) {
    }

    @Around("pointCut(jfLog)")
    public Object around(ProceedingJoinPoint joinPoint, JFLog jfLog) throws Throwable {
        long startTime = System.currentTimeMillis();

        JFLogServerPojo jfLogServerPojo = new JFLogServerPojo();
        // 设置开始时间
        jfLogServerPojo.setStart(startTime);
        // 设置API code
        jfLogServerPojo.setApicode(jfLog.value());
        if(StringUtils.hasText(jfLog.apiCode())){
            jfLogServerPojo.setApicode(jfLog.apiCode());
        }
        // 设置是否计费标识
        jfLogServerPojo.setFlag(jfLog.feeFlag());

        //获取响应参数
        Object result = joinPoint.proceed();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        //获取请求参数
        Object reqParams = getParameter(method, joinPoint.getArgs());
        String jsonStr = JacksonUtils.toJson(reqParams);
        ProviderQuery providerQuery = JacksonUtils.toPojo(jsonStr, ProviderQuery.class);

        setJfLogServerPojo(jfLogServerPojo, providerQuery);

        // 设置结束时间
        long endTime = System.currentTimeMillis();
        jfLogServerPojo.setEnd(endTime);
        LOGGER.info(JacksonUtils.toJson(jfLogServerPojo));
        return result;
    }

    private JFLogServerPojo setJfLogServerPojo(JFLogServerPojo jfLogServerPojo, ProviderQuery providerQuery) {
        //获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String jfSys = request.getHeader("JF_SYS"); // 系统标识
        String jfMu = request.getHeader("JF_MU");     // 模块标识
        String jfRn = request.getHeader("JF_RN");     // 请求ID

        // 设置请求应用名称
        jfLogServerPojo.setRsys(jfSys);
        if(!StringUtils.hasText(jfSys)) {
            jfLogServerPojo.setRsys(providerQuery.getAppName());
        }
        // 设置请求模块名称
        jfLogServerPojo.setRmu(jfMu);
        if(!StringUtils.hasText(jfMu)) {
            jfLogServerPojo.setRmu(providerQuery.getModelName());
        }
        // 设置请求id, 头中没请求id则设置为UUID
        jfLogServerPojo.setRid(jfRn);
        if(!StringUtils.hasText(jfRn)) {
            jfLogServerPojo.setRid(UUID.randomUUID().toString());
        }
        // 设置响应id
        String traceId = MDC.get("traceId");
        jfLogServerPojo.setSid(traceId);

        return jfLogServerPojo;
    }

    /**
     * 根据方法和传入的参数获取请求参数
     */
    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (StringUtils.hasText(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }

}
